import {
    MeshLambertMaterial, Mesh, ExtrudeGeometry, Shape, ArcCurve
} from 'three';

export default class PieChart {
    public group: Mesh;
    private ladder: boolean;
    private separation: boolean;
    private loop: boolean;
    private concentric: boolean;
    private concentricLoop: boolean;
    private concentricLoopSame: boolean;

    constructor(data: Size[] = [], manifest: Manifest) {
        const sumAngleArr = this.getSumAngle(data);
        const { ladder, separation, loop, concentric, concentricLoop, concentricLoopSame = true } = manifest;
        this.ladder = (ladder as boolean);
        this.separation = (separation as boolean);
        this.loop = (loop as boolean);
        this.concentric = (concentric as boolean);
        this.concentricLoop = (concentricLoop as boolean);
        this.concentricLoopSame = (concentricLoopSame as boolean)
        this.group = new Mesh();
        data.forEach((item, index) => {
            const circleMesh = this.drawCircle(item, sumAngleArr[index], index);
            if (this.concentric) {
                circleMesh.position.y += index * 0.2;
            }
            this.group.add(circleMesh);
            this.group.position.z = 0.5;
        });
    }

    // 获取绕y轴偏转角度
    private getSumAngle(data: Size[]): number[] {
        const angleArr = data.map((item) => item.angle);
        const rotationAngle = angleArr.reduce((acc: number[], cur, index) => {
            if (index === 0) {
                acc.push(cur);
            } else {
                acc.push(cur + acc[index - 1]);
            }
            return acc;
        }, []);
        rotationAngle.pop();
        rotationAngle.unshift(0);
        return rotationAngle;
    }
    /**
     * @description 画蛋糕
     * @param item
     * @param preRotation
     * @returns
     */
    private drawCircle(item: Size, preRotation: number, index: number) {
        const { radius, angle, height, color } = item;
        let shape = new Shape();
        let h = height;
        if (this.loop) {
            shape = this.drawLoopShape(shape, radius, angle)
        } else if (this.concentric) {
            h = 0.2;
            shape = this.drawConcentricShape(shape, radius, angle)
        } else if (this.concentricLoop) {
            shape = this.drawConcentricLoopShape(shape, radius, angle, index, preRotation)
        } else {
            shape = this.drawNormalShape(shape, radius, angle)
        }
        const extrudeSettings = {
            curveSegments: 1000,
            steps: 1,
            depth: this.ladder ? h : 0.2,
            // depth: 0,
            bevelEnabled: false,
            bevelThickness: 0,
            bevelSize: 0,
            bevelOffset: 0,
            bevelSegments: 0
        };

        const geometry = new ExtrudeGeometry(shape, extrudeSettings);
        const material = new MeshLambertMaterial({ color, depthTest: true });
        const mesh = new Mesh(geometry, material);

        mesh.rotation.x = -Math.PI / 2;
        if (this.concentricLoop && this.concentricLoopSame) {
        } else {
            mesh.rotation.z = preRotation;
        }

        return mesh;
    }

    private drawLoopShape(shape: Shape, radius: number, angle: number) {
        let originPoint = [0, 0];
        if (this.separation) {
            originPoint = [0.04, 0.04]
        }
        shape.moveTo(originPoint[0], originPoint[1])
        const curOuter = new ArcCurve(originPoint[0], originPoint[1], radius, 0, angle, false);
        const curInner = new ArcCurve(originPoint[0], originPoint[1], radius / 2, 0, angle, false);

        const pointsOuter = curOuter.getPoints(50).reverse();
        const pointsInner = curInner.getPoints(50);

        shape.moveTo(radius / 2 + originPoint[0], originPoint[1]);
        for (const i of pointsInner) {
            shape.lineTo(i.x, i.y)
        }
        shape.lineTo(radius * Math.cos(angle) + originPoint[0], radius * Math.sin(angle) + originPoint[1])
        for (const i of pointsOuter) {
            shape.lineTo(i.x, i.y)
        }
        shape.lineTo(radius / 2 + originPoint[0], originPoint[1])
        return shape;
    }

    private drawNormalShape(shape: Shape, radius: number, angle: number) {
        let originPoint = [0, 0];
        if (this.separation) {
            originPoint = [0.04, 0.04]
        }
        shape.moveTo(originPoint[0], originPoint[1])
        shape.arc(0, 0, radius, 0, angle, false)
        if (this.separation) {
            shape.lineTo(0.04, 0.04)
        } else {
            shape.lineTo(0, 0);
        }
        return shape;
    }

    private drawConcentricShape(shape: Shape, radius: number, angle: number) {
        shape.arc(0, 0, radius / 2 * angle, 0, Math.PI * 2, false)
        return shape;
    }

    private drawConcentricLoopShape(shape: Shape, radius: number, angle: number, index: number, preRotation: number) {
        const r = radius / 3;
        const deg = angle * 2;
        const curOuter = new ArcCurve(0, 0, r * deg + 0.1, 0, deg, false);
        const curInner = new ArcCurve(0, 0, r * deg, 0, deg, false);

        const pointsOuter = curOuter.getPoints(50).reverse();
        const pointsInner = curInner.getPoints(50);

        shape.moveTo(r * deg, 0);
        for (const i of pointsInner) {
            shape.lineTo(i.x, i.y)
        }
        shape.lineTo((r * deg + 0.1) * Math.cos(deg), (r * deg + 0.1) * Math.sin(deg))
        for (const i of pointsOuter) {
            shape.lineTo(i.x, i.y)
        }
        shape.lineTo(r * deg + 0.1, 0)

        // index
        return shape;
    }
}
